package apibasej.basic.misc;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Level;

import apibasej.basic.config.ConfigManager;

public class UtilTrace{

	// copiado dos writers como System.out.println();
	public static String getLineSeparator(){
		System.out.println();
		return (String) java.security.AccessController.doPrivileged(
	               new sun.security.action.GetPropertyAction("line.separator"));
	}
	public static final String getStackTrace(String myMess, Throwable exception) {
		return myMess+getLineSeparator()+getStackTrace(exception);
	}
	public static final String getStackTrace(Throwable exception) {
		if(exception==null){
			return null;
		}
		try(
			StringWriter writer = new StringWriter();
			PrintWriter printWriter = new PrintWriter(writer);
			){
			exception.printStackTrace(printWriter);
			return writer.toString();
		} catch (Throwable e) {
			String m = "Error on create printer for Exception";
			if(isRecursiveCall(e)){
				System.out.println("### UtilTrace.UtilTrace() RECURSIVE CALL FOR: "+e+" ###");
				e.printStackTrace();
			}else{
				//UtilLog.log(UtilTrace.class,WARN_ON_CLOSE_WRITER_FOR_STACK_TRACE,m,e);
				ConfigManager.logDefAll(Level.WARNING, m, e);
			}
			return "ERRO: "+exception+" ["+m+": "+e+"]";
		}
	}
	
	
	public static String getStrTrace(StackTraceElement[] st){
		StringBuilder sb = new StringBuilder();
		for(StackTraceElement s : st){
			sb.append(s.toString());
			sb.append('\n');
		}
		return sb.toString();
	}
	
	
	// para evitar loops infinitos !!!!
	// para verificar no trace do método que chamou esta função teve alguma chamada anterior para o próprio método
	// !!! não mudar o nome deste método ou mudar de classe !!! por causa das string de validação!!!
	public static boolean isRecursiveCall(Throwable t){ //String callMethodName, Class<?> callClass, 
		try{
			//EX:
			// java.lang.Thread  getStackTrace
			// apibasej.basic.misc.UtilTrace  isRecursiveCall
			// juliolessa.sisadmin.server.LogServer  salvarLog <<< a classe chamada srá sempre o terceiro item!?
			// ...
			StackTraceElement[] callTrace = Thread.currentThread().getStackTrace();
			
			if(callTrace.length<3){//não deve ocorrer?
				IllegalStateException ex = new IllegalStateException("Thread.currentThread().getStackTrace().length < 3 !!!");
				ConfigManager.logDefAll(Level.SEVERE, ex.getMessage(), ex);
				return false;
			}
			if(!callTrace[0].getClassName().equals(Thread.class.getName())
					|| !callTrace[0].getMethodName().equals("getStackTrace")
					|| !callTrace[1].getClassName().equals(UtilTrace.class.getName())
					|| !callTrace[1].getMethodName().equals("isRecursiveCall")){
				//valida por garantia
				throw new IllegalStateException("Unexpected values ​​to position 0 and 1 from stack trace! [callTrace[0].getClassName()="+callTrace[0].getClassName()+",callTrace[0].getMethodName()="+callTrace[0].getMethodName()+",callTrace[1].getClassName()="+callTrace[1].getClassName()+",callTrace[1].getMethodName()="+callTrace[1].getMethodName()+"]");
				
			}
			String callerClassName = callTrace[2].getClassName();
			String callerMethodName = callTrace[2].getMethodName();
			
			for(int i=0; i<callTrace.length; i++){
				if(i<3){
					//só começa a procurar a partir da quarta posição do array (i=3)
					continue;
				}
				StackTraceElement st = callTrace[i];
				if(st.getClassName().equals(callerClassName) && st.getMethodName().equals(callerMethodName)){
					return true;
				}
			}
			
			//se receber uma exceção valida também se tem chamada recursiva no trace da exceção
			while(t!=null){
				for(StackTraceElement st : t.getStackTrace()){
					if(st.getClassName().equals(callerClassName) && st.getMethodName().equals(callerMethodName)){
						return true;
					}
				}
				t = t.getCause();
			}
			return false;
		}catch(Throwable t2){
			ConfigManager.logDefAll(Level.SEVERE, "ERROR ON UtilTrace.isRecursiveCall(...)!!!", t2);
			return false;
		}
	}
	
}
